package com.siss.tenancy.elastic.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.siss.tenancy.constant.SissEnumUtil;
import com.siss.tenancy.elastic.entity.SissPerson;
import com.siss.tenancy.elastic.model.SissMontorEsModel;
import com.siss.tenancy.elastic.repository.SissMonitorEsRepository;
import com.siss.tenancy.elastic.utils.RedisUtil;
import com.siss.tenancy.finals.ElasticIndex;
import com.siss.tenancy.util.MapUtils;
import com.siss.tenancy.util.Object2JsonString2ObjectUtil;
import com.siss.tenancy.util.StringUtil;
import com.siss.tenancy.vo.PageResult;
import com.siss.tenancy.vo.TableResult;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SissMonitorEsImp {

    @Autowired
    private SissMonitorEsRepository sissMonitorEsRepository;


    @Autowired
    RedisUtil redisUtil;

    @Autowired
    RestHighLevelClient restHighLevelClient;

    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;


    @Autowired
    SissPersonService sissPersonService;


    private final RequestOptions options = RequestOptions.DEFAULT;

    /**
     * 内置排序规则，用户类型正序排序，星级倒序排序
     * @param keyWord
     * @param type
     * @param pageSize
     * @param pageNo
     * @pageEable 是否分页，默认false
     * @return
     */
    public Object selectByPage(boolean hili,String keyWord, Integer type,String[] actorTypes,String[] districts,
                               Integer[] starLevels,Integer[] recordStatus,Boolean record,Integer black,Integer leagueType,
                               int pageSize, int pageNo,boolean pageEable) throws IOException {
        if (ObjectUtils.isEmpty(pageNo)) {
            pageNo = 0;
        }else{
            pageNo = pageNo-1;
        }
        if (ObjectUtils.isEmpty(pageSize)) {
            pageSize = 10;
        }
        if(null != type && 2 == type){
            //搜索楼盘
            return selectHouseNyPage( keyWord,districts,pageSize, pageNo);
        }
        BoolQueryBuilder keyBuilder = QueryBuilders.boolQuery();
        if(!StringUtil.isEmpty(keyWord)){
            keyWord = "*"+keyWord+"*";
            //名字
            keyBuilder.should(QueryBuilders.wildcardQuery("name",keyWord));
            //机构名字
            keyBuilder.should(QueryBuilders.wildcardQuery("organName",keyWord));
            //分支机构名字
            keyBuilder.should(QueryBuilders.wildcardQuery("branchName",keyWord));
            //法人
            keyBuilder.should(QueryBuilders.wildcardQuery("legalPerson",keyWord));
            //身份证号或者信用代码
            keyBuilder.should(QueryBuilders.wildcardQuery("code",keyWord));
            //加盟公司名字
            keyBuilder.should(QueryBuilders.wildcardQuery("leagueName",keyWord));
            //备案地址
            keyBuilder.should(QueryBuilders.wildcardQuery("address",keyWord));
            //营业地址
            keyBuilder.should(QueryBuilders.wildcardQuery("businessAddress",keyWord));
            //手机号
            keyBuilder.should(QueryBuilders.wildcardQuery("mobile",keyWord));
            //牌号
            keyBuilder.should(QueryBuilders.wildcardQuery("licenseNo",keyWord));
        }else{
            keyBuilder.should(QueryBuilders.matchAllQuery());
        }
        //组装查询
        SearchSourceBuilder searchQueryBuilder = new SearchSourceBuilder()
                .from(pageNo).size(pageSize)
                .sort(SortBuilders.fieldSort("actorType").order(SortOrder.ASC))
                .sort(SortBuilders.fieldSort("starLevel").order(SortOrder.DESC))
                .query(keyBuilder);
        //组装过滤器
        installFilter(searchQueryBuilder,actorTypes,districts,starLevels,recordStatus,record,black,leagueType);
        List<String> his = new ArrayList<>();
        his.add("name");
       /* if(hili){
            return hilightResult(keyBuilder,searchQueryBuilder,his,pageEable,pageSize);
        }*/
        SearchRequest searchRequest = new SearchRequest(ElasticIndex.MONTOR);
        HiLight(searchQueryBuilder,his);
        searchRequest.source(searchQueryBuilder);
        SearchResponse searchResp  = restHighLevelClient.search(searchRequest, options);
        List<SissMontorEsModel> list = new ArrayList<>() ;
        SearchHit[] searchHitArr = searchResp.getHits().getHits();
        if(null == searchHitArr && searchHitArr.length<=0){
            return null;
        }
        SissMontorEsModel s = null;
        for (SearchHit searchHit:searchHitArr){
            Map<String,Object> temp = searchHit.getSourceAsMap();
            s = (SissMontorEsModel)MapUtils.mapToBean(temp,SissMontorEsModel.class);
            if(StringUtil.isEmpty(s.getSaId())){
                s.setSaId(searchHit.getId());
            }
            //获取对应的高亮域
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            //高亮字段
            HighlightField highlightAddress = highlightFields.get("name");
            if(highlightAddress!=null){
                // 为title串值增加自定义的高亮标签
                Text[] titleTexts = highlightAddress.fragments();
                String address = "";
                for (Text text : titleTexts) {
                    address += text;
                }
                //将追加了高亮标签的串值重新填充到对应的对象
                s.setHiLightName(address);
            }
            list.add(s);
        }
        hidePersonIdNoAndMobile(list);
        if(pageEable){
            com.github.pagehelper.Page pager = new com.github.pagehelper.Page();
            BeanUtils.copyProperties(list,pager);
            pager.clear();
            pager.addAll(list);
            pager.setTotal(searchResp.getHits().getTotalHits().value);
            long totalPage = searchResp.getHits().getTotalHits().value%pageSize==0?
                    searchResp.getHits().getTotalHits().value/pageSize :(1+searchResp.getHits().getTotalHits().value/pageSize);
            pager.setPageNum(Integer.valueOf(totalPage+""));
            pager.setPageSize(pageSize);
            return new PageResult(pager);
        }else{
            return new TableResult<>(list);
        }
    }

    private Object hilightResult(BoolQueryBuilder keyBuilder, SearchSourceBuilder searchQueryBuilder, List<String> his,boolean pageEable,int pageSize) {
        NativeSearchQueryBuilder searchQuery = new NativeSearchQueryBuilder()
                .withQuery(keyBuilder)
                .withFilter(searchQueryBuilder.postFilter())
                .withHighlightFields( new HighlightBuilder.Field("name"))
                .withHighlightBuilder(new HighlightBuilder().preTags("<span style='color:red'>").postTags("</span>"))
                ;
//        HiLight(searchQuery,his);
        //查询
        SearchHits<SissMontorEsModel> search = elasticsearchRestTemplate.search(searchQuery.build(), SissMontorEsModel.class,IndexCoordinates.of(ElasticIndex.MONTOR));
        //得到查询返回的内容
        List<org.springframework.data.elasticsearch.core.SearchHit<SissMontorEsModel>> searchHits = search.getSearchHits();
        List<SissMontorEsModel> esModels = new ArrayList<>();
        for(org.springframework.data.elasticsearch.core.SearchHit<SissMontorEsModel> searchHit:searchHits){
            //高亮的内容
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            //将高亮的内容填充到content中
            searchHit.getContent().setHiLightName(highlightFields.get("name")==null ? searchHit.getContent().getName():highlightFields.get("name").get(0));
            //放到实体类中
            esModels.add(searchHit.getContent());
        }
        if(pageEable){
            com.github.pagehelper.Page pager = new com.github.pagehelper.Page();
            BeanUtils.copyProperties(esModels,pager);
            pager.clear();
            pager.addAll(esModels);
            pager.setTotal(search.getTotalHits());
            pager.setPageSize(pageSize);
            return new PageResult(pager);
        }
        return esModels;
    }

    public Object selectHouseNyPage(String  keyWord,String[] districts,int pageSize,int pageNo) throws IOException {
        BoolQueryBuilder keyBuilder = QueryBuilders.boolQuery();
        if(!StringUtil.isEmpty(keyWord)){
            //楼盘名字
            keyBuilder.should(QueryBuilders.matchPhrasePrefixQuery("name",keyWord));
            keyBuilder.should(QueryBuilders.matchPhrasePrefixQuery("address",keyWord));
        }else{
            keyBuilder.should(QueryBuilders.matchAllQuery());
        }
        SearchSourceBuilder searchQueryBuilder = new SearchSourceBuilder()
                .from(pageNo).size(pageSize)
                .sort(SortBuilders.fieldSort("special").order(SortOrder.DESC))
                .query(keyBuilder);
        if(null != districts && districts.length>0){
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            BoolQueryBuilder districtBooleanQuery = QueryBuilders.boolQuery();
            for (String district : districts) {
                districtBooleanQuery.should(QueryBuilders.termQuery("district",district));
            }
            boolQueryBuilder.must(districtBooleanQuery);
            searchQueryBuilder.postFilter(boolQueryBuilder);
        }
        SearchRequest searchRequest = new SearchRequest(ElasticIndex.MONTOR);
        List<String> his = new ArrayList<>();
        his.add("name");
        HiLight(searchQueryBuilder,his);
        searchRequest.source(searchQueryBuilder);
        SearchResponse searchResp  = restHighLevelClient.search(searchRequest, options);
        List<SissMontorEsModel> list = new ArrayList<>() ;
        SearchHit[] searchHitArr = searchResp.getHits().getHits();
        if(null == searchHitArr && searchHitArr.length<=0){
            return null;
        }
        SissMontorEsModel s = null;
        for (SearchHit searchHit:searchHitArr){
            Map<String,Object> temp = searchHit.getSourceAsMap();
            s = (SissMontorEsModel)MapUtils.mapToBean(temp,SissMontorEsModel.class);
            if(StringUtil.isEmpty(s.getSaId())){
                s.setSaId(searchHit.getId());
            }
            //获取对应的高亮域
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            //高亮字段
            HighlightField highlightAddress = highlightFields.get("name");
            if(highlightAddress!=null){
                // 为title串值增加自定义的高亮标签
                Text[] titleTexts = highlightAddress.fragments();
                String address = "";
                for (Text text : titleTexts) {
                    address += text;
                }
                //将追加了高亮标签的串值重新填充到对应的对象
                s.setHiLightName(address);
            }
            list.add(s);
        }
        hidePersonIdNoAndMobile(list);
        com.github.pagehelper.Page pager = new com.github.pagehelper.Page();
        BeanUtils.copyProperties(list,pager);
        pager.clear();
        pager.addAll(list);
        pager.setTotal(searchResp.getHits().getTotalHits().value);
        long totalPage = searchResp.getHits().getTotalHits().value%pageSize==0?
                searchResp.getHits().getTotalHits().value/pageSize :(1+searchResp.getHits().getTotalHits().value/pageSize);
        pager.setPageNum(Integer.valueOf(totalPage+""));
        pager.setPageSize(pageSize);
        return new PageResult(pager);
    }

    public void installFilter(SearchSourceBuilder nativeSearchQueryBuilder, String[] actorTypes, String[] districts,
                              Integer[] starLevels, Integer[] recordStatus, Boolean record, Integer black, Integer leagueType){
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(null!= actorTypes && actorTypes.length>0){
            BoolQueryBuilder actorBooleanQuery = QueryBuilders.boolQuery();
            for (String actorType : actorTypes) {
                actorBooleanQuery.should(QueryBuilders.termQuery("actorType",actorType));
            }
            boolQueryBuilder.must(actorBooleanQuery);
        }
        if(null!= districts && districts.length>0){
            BoolQueryBuilder districtBooleanQuery = QueryBuilders.boolQuery();
            for (String district : districts) {
                districtBooleanQuery.should(QueryBuilders.termQuery("district",district));
            }
            boolQueryBuilder.must(districtBooleanQuery);
        }
        if(null!= starLevels && starLevels.length>0){
            BoolQueryBuilder starLevelBooleanQuery = QueryBuilders.boolQuery();
            for (Integer starLevel : starLevels) {
                starLevelBooleanQuery.should(QueryBuilders.termQuery("starLevel",starLevel));
            }
            boolQueryBuilder.must(starLevelBooleanQuery);
        }
        if(null!= recordStatus && recordStatus.length>0){
            BoolQueryBuilder starLevelBooleanQuery = QueryBuilders.boolQuery();
            for (Integer recordStatu : recordStatus) {
                starLevelBooleanQuery.should(QueryBuilders.termQuery("recordStatus",recordStatu));
            }
            boolQueryBuilder.must(starLevelBooleanQuery);
        }
        if(null != record){
            boolQueryBuilder.must(QueryBuilders.matchQuery("record",record));
        }
        if(null != black){
            BoolQueryBuilder blackBooleanQuery = QueryBuilders.boolQuery();
            blackBooleanQuery.should(QueryBuilders.termQuery("black",black));
            boolQueryBuilder.must(blackBooleanQuery);
        }
        if(null != leagueType){
            BoolQueryBuilder leagueTypeBooleanQuery = QueryBuilders.boolQuery();
            leagueTypeBooleanQuery.should(QueryBuilders.termQuery("leagueType",leagueType));
            boolQueryBuilder.must(leagueTypeBooleanQuery);
        }
        boolQueryBuilder.must(QueryBuilders.matchQuery("deleted",0));
        nativeSearchQueryBuilder.postFilter(boolQueryBuilder);
//        nativeSearchQueryBuilder.withFilter(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("deleted",false)));
    }


    private void hidePersonIdNoAndMobile(List<SissMontorEsModel> list){
        if(null != list){
            String code = null;
            String mobile =  null;
            List<String> saIds = list.stream().filter(e -> e.getActorType().equals(SissEnumUtil.SRBA_ACTOR_TYPE.RY.getDict_value())).map(SissMontorEsModel :: getSaId).collect(Collectors.toList());
            List<SissPerson> persons = null;
            Map<String,Boolean> personShow = null;
            if(!saIds.isEmpty()){
                persons = sissPersonService.selectBySaIds(saIds);
                if(null != persons && !persons.isEmpty()){
                    personShow = persons.stream().collect(Collectors.toMap(SissPerson :: getSaId,SissPerson :: getPrivacyShow));
                }

            }
            for (SissMontorEsModel sissMontorEsModel : list) {
                if(null != personShow){
                    if(null != personShow.get(sissMontorEsModel.getSaId())){
                        code = sissMontorEsModel.getCode();
                        String prefix = code.substring(0, 6);
                        String suffix = code.substring(code.length() - 5, code.length());
                        sissMontorEsModel.setCode(prefix + "********" + suffix);
                        if(!personShow.get(sissMontorEsModel.getSaId())){
                            mobile = sissMontorEsModel.getMobile();
                            if(StringUtil.isEmpty(mobile) || mobile.length()<11){
                                continue;
                            }
                            String prefixM = mobile.substring(0, 3);
                            String suffixM = mobile.substring(mobile.length() - 3, mobile.length());
                            sissMontorEsModel.setMobile(prefixM + "*****" + suffixM);
                        }
                    }
                }
            }
        }

    }

    public void HiLight(NativeSearchQueryBuilder searchSourceBuilder,List<String> hilightField){
        for (String s : hilightField) {
            searchSourceBuilder.withHighlightFields(new HighlightBuilder.Field(s));
        }
    }

    public void HiLight(SearchSourceBuilder searchSourceBuilder,List<String> hilightField){
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        for (String s : hilightField) {
            //构建高亮查询
            HighlightBuilder.Field field = new HighlightBuilder.Field(s);
//            field.highlighterType("unified");
            highlightBuilder.field(field);
        }
        searchSourceBuilder.highlighter(highlightBuilder);
    }

    public void batchCreateMonitor(List<SissMontorEsModel> models){
        sissMonitorEsRepository.saveAll(models);
    }

    public SissMontorEsModel selectById(String id){
        return sissMonitorEsRepository.queryEsInfoModelBySaId(id);
    }

    public void save(SissMontorEsModel sissMontorEsModel){
        sissMonitorEsRepository.save(sissMontorEsModel);
    }


    public Integer testSeata(Integer noTrans) {
        return sissPersonService.testSeata(noTrans);

    }
}
